package server;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import server.components.Context;
import server.components.Host;
import server.components.Mapper;
import server.components.Wrapper;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.*;
import java.util.concurrent.*;

public class Bootstrap {

    // 定义socket监听的端口号
    private int port;

    private Mapper mapper = new Mapper();

    /**
     * Minicat启动需要展开的一系列操作
     */
    public void start() throws Exception {

        // 加载配置文件 server.xml
        loadServer();

        // 定义一个线程池
        int corePoolSize = 10;
        int maximumPoolSize = 50;
        long keepAliveTime = 100L;
        TimeUnit unit = TimeUnit.SECONDS;
        BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(50);
        ThreadFactory threadFactory = Executors.defaultThreadFactory();
        RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();

        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                corePoolSize,
                maximumPoolSize,
                keepAliveTime,
                unit,
                workQueue,
                threadFactory,
                handler
        );

        ServerSocket serverSocket = new ServerSocket(port);
        System.out.println("=====>>>Minicat start on port：" + port);

        /**
         * 完成Minicat 4.0版本
         * 需求：可以请求动态资源（Servlet）,模拟出webapps部署效果
         * 磁盘上放置⼀个webapps⽬录，webapps中可以有多个项⽬，⽐如demo1,demo2,demo3...
         * 具体的项⽬⽐如demo1中有serlvet（也即为：servlet是属于具体某⼀个项⽬的servlet），这样的话在
         * Minicat初始化配置加载，以及根据请求url查找对应serlvet时都需要进⼀步处理
         */
        while(true) {
            Socket socket = serverSocket.accept();
            RequestProcessor requestProcessor = new RequestProcessor(socket, mapper);
            threadPoolExecutor.execute(requestProcessor);
        }
    }

    /**
     * 加载解析相关的配置server.xml
     */
    public void loadServer() {
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("server.xml");
        SAXReader saxReader = new SAXReader();
        try {
            // 转换为文档对象
            Document document = saxReader.read(resourceAsStream);
            // 获取根节点
            Element rootElement = document.getRootElement();
            // 获取端口号并赋值给port
            Element connector = (Element) rootElement.selectSingleNode("//Connector");
            this.port = Integer.parseInt(connector.attributeValue("port"));

            // 获取host中配置的appBase，appBase中表示部署项目的绝对路径
            List<Element> hostList = rootElement.selectNodes("//Host");
            if (hostList != null || !hostList.isEmpty()) {
                for (Element element : hostList) {
                    // 封装主机信息
                    Host host = new Host();
                    String hostName = element.attributeValue("name");
                    String appBase = element.attributeValue("appBase");
                    host.setName(hostName);
                    // 获取绝对路径下所有项目，projects:demo1、demo2...
                    File[] projects = new File(appBase).listFiles();
                    // 遍历webapps下的项目信息
                    for (File project : projects) {
                        if (project.isDirectory()) {
                            // 封装项目信息
                            Context context = new Context();
                            // projectName:demo1
                            String projectName = project.getName();
                            context.setPath(projectName);
                            // 实例化项目下的web.xml文件
                            File webFile = new File(project, "web.xml");
                            // 加载解析web.xml，实例化wrapper信息。（注：File.separator表示Linux下的分隔符）
                            List<Wrapper> wrappers = loadServlet(webFile, appBase + File.separator + projectName);
                            context.setWrappers(wrappers);
                            host.getContexts().add(context);
                        }
                    }
                    // 封装mapper信息
                    mapper.getHosts().add(host);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 加载解析web.xml，初始化Servlet，返回Wrapper列表
     * @param webFile web.xml的File类型文件
     * @param appBase 项目的绝对路径
     * @return wrappers
     */
    public List<Wrapper> loadServlet(File webFile, String appBase) {
        try {
            // 用于存放包含servlet信息的wrapper，且作为该方法的返回值
            List<Wrapper> wrappers = new LinkedList<>();
            // 获取web.xml的输入流
            InputStream resourceAsStream = new FileInputStream(webFile);
            SAXReader saxReader = new SAXReader();
            Document document = saxReader.read(resourceAsStream);
            Element rootElement = document.getRootElement();
            List<Element> selectNodes = rootElement.selectNodes("//servlet");
            for (int i = 0; i < selectNodes.size(); i++) {
                Element element = selectNodes.get(i);
                // <servlet-name>lagou</servlet-name>
                Element servletNameElement = (Element) element.selectSingleNode("servlet-name");
                String servletName = servletNameElement.getStringValue();
                // <servlet-class>server.LagouServlet</servlet-class>
                Element servletClassElement = (Element) element.selectSingleNode("servlet-class");
                String servletClass = servletClassElement.getStringValue();
                // 根据servletName的值找到url-pattren
                Element servletMapping = (Element) rootElement.selectSingleNode("/web-app/servlet-mapping[servlet-name='" + servletName + "']");
                // /lagou
                String urlPattern = servletMapping.selectSingleNode("url-pattern").getStringValue();
                //Class<?> clazz = ((MyClassLoader) classLoader).findClass(servletClass);
                String classLoaderPath = appBase + "/" +  servletClass.replace(".", "/") + ".class";
                // 获取项目的类加载器
                MyClassLoader myClassLoader = new MyClassLoader(classLoaderPath);
                Class<?> clazz = myClassLoader.findClass(servletClass);
                // 封装wrapper信息，并存放于wrapper列表中
                Wrapper wrapper = new Wrapper();
                wrapper.setUrl(urlPattern);
                wrapper.setServlet((HttpServlet) clazz.newInstance());
                wrappers.add(wrapper);
            }
            return wrappers;
        } catch (DocumentException | FileNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
        return null;
    }
}
